package ximg

import (
	"errors"
	"image"
	"mime/multipart"
	"os"
	"strings"

	"gitee.com/xuesongtao/gotool/internal"
	"gitee.com/xuesongtao/gotool/xdebug"
	"gitee.com/xuesongtao/gotool/xfile"
	"github.com/nfnt/resize"
)

const (
	_                uint8 = iota
	DirectResize           // 直接压缩
	EqualRatioResize       // 等比压缩
)

type XImg struct {
	src      multipart.File
	SysSplit string                        // 系统路径分隔符
	FileName string                        // 文件名
	handleFn func(image.Image) image.Image // 图片处理
}

// NewXImg 初始化
func NewXImg(src interface{}) (*XImg, error) {
	obj := &XImg{
		SysSplit: xdebug.GetSysPathSplitSymbol(),
	}
	var err error
	switch v := src.(type) {
	case string:
		tmpIndex := strings.LastIndex(v, obj.SysSplit)
		obj.FileName = v
		if tmpIndex != -1 {
			obj.FileName = v[tmpIndex+1:]
		}

		obj.src, err = xfile.Open(v)
		return obj, err
	case *multipart.FileHeader: // 支持 web 框架
		obj.FileName = v.Filename
		obj.src, err = v.Open()
		return obj, err
	}
	return nil, errors.New("no support")
}

// getSrc 获取 src 文件句柄
func (x *XImg) getSrc() (multipart.File, image.Config, error) {
	conf := image.Config{}
	config, fileFmt, err := image.DecodeConfig(x.src)
	if err != nil {
		return nil, conf, errors.New("image.DecodeConfig is failed, err:" + err.Error())
	}
	x.src.Seek(0, 0) // https://github.com/golang/go/issues/50992
	internal.Infof("file w: %d, h: %d, fmt: %s, filename: %s", config.Width, config.Height, fileFmt, x.FileName)
	return x.src, config, nil
}

// getDest 获取 dest 句柄
func (x *XImg) getDest(dest string) (*os.File, error) {
	// internal.Info("dest: ", dest)
	out, err := xfile.Create(dest)
	if err != nil {
		return nil, errors.New("os.Create is failed, err: " + err.Error())
	}
	// defer out.Close()
	return out, nil
}

// GetImgFmt 获取文件格式
func (x *XImg) GetImgFmt() string {
	return strings.ToLower(xfile.ParseFileFormat(x.FileName))
}

// Compress 压缩图片
// dest 为目标文件路径
// quality 为压缩比, 如果不想设置请设置 -1, 暂时只支持 jpeg, jpg
func (x *XImg) Compress(dest string, quality int) (destFileName string, err error) {
	in, config, err := x.getSrc()
	if err != nil {
		return
	}
	defer in.Close()

	out, err := x.getDest(dest)
	if err != nil {
		return
	}
	defer out.Close()

	destFileName = out.Name()

	// 压缩
	compressFn := func(img image.Image) image.Image {
		if x.handleFn != nil {
			return x.handleFn(resize.Resize(uint(config.Width), uint(config.Height), img, resize.Lanczos3))
		}
		return resize.Resize(uint(config.Width), uint(config.Height), img, resize.Lanczos3)
	}
	switch x.GetImgFmt() {
	case "png":
		err = Png(out, in, compressFn)
		return
	case "jpeg", "jpg":
		err = Jpeg(out, in, quality, compressFn)
		return
	case "gif":
		err = Gif(out, in, nil, compressFn)
		return
	case "bmp":
		err = Bmp(out, in, compressFn)
		return
	case "webp":
		err = Webp(out, in, compressFn)
		return
	}

	err = errors.New("no support compress \"" + x.FileName + "\"")
	return
}

// SetHandleFn 这种处理函数
func (x *XImg) SetHandleFn(fn func(image.Image) image.Image) {
	x.handleFn = fn
}

// Resize 修改尺寸
// dest 为目标文件路径
// newW 新的宽度
// newH 新的高度
// quality 为压缩比, 如果不想设置请设置 -1, 暂时只支持 jpeg, jpg
// resizeType 压缩类型
func (x *XImg) Resize(dest string, newW, newH uint, quality int, resizeType ...uint8) (destFileName string, err error) {
	in, config, err := x.getSrc()
	if err != nil {
		return
	}
	defer in.Close()

	out, err := x.getDest(dest)
	if err != nil {
		return
	}
	defer out.Close()

	defaultResizeType := EqualRatioResize
	if len(resizeType) > 0 {
		defaultResizeType = resizeType[0]
	}
	resizeFn := func(img image.Image) image.Image {
		return img
	}
	switch defaultResizeType {
	case EqualRatioResize:
		resizeFn = func(img image.Image) image.Image {
			base := int(newW)
			height := base * config.Height / config.Width
			if x.handleFn != nil {
				return x.handleFn(resize.Thumbnail(uint(base), uint(height), img, resize.Lanczos3))
			}
			return resize.Thumbnail(uint(base), uint(height), img, resize.Lanczos3)
		}
	case DirectResize:
		resizeFn = func(img image.Image) image.Image {
			if x.handleFn != nil {
				return x.handleFn(resize.Resize(newW, newH, img, resize.Lanczos3))
			}
			return resize.Resize(newW, newH, img, resize.Lanczos3)
		}
	}

	destFileName = out.Name()
	switch x.GetImgFmt() {
	case "png":
		err = Png(out, in, resizeFn)
		return
	case "jpeg", "jpg":
		err = Jpeg(out, in, quality, resizeFn)
		return
	case "gif":
		err = Gif(out, in, nil, resizeFn)
		return
	case "bmp":
		err = Bmp(out, in, resizeFn)
		return
	case "webp":
		err = Webp(out, in, resizeFn)
		return
	}
	err = errors.New("no support resize \"" + x.FileName + "\"")
	return
}

// GetSize 获取文件大小
func GetSize(path string) (int64, error) {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return 0, err
	}
	return fileInfo.Size(), nil
}

// ValidPicFmt 验证图片格式
func ValidPicFmt(filename string, mustFmt ...[]string) bool {
	picFmt := xfile.ParseFileFormat(filename)
	defaultFmt := []string{"png", "jpeg", "jpg", "bmp", "webp"}
	if len(mustFmt) > 0 {
		defaultFmt = mustFmt[0]
	}
	for _, v := range defaultFmt {
		if v == picFmt {
			return true
		}
	}
	return false
}
